#include "core/bus.h"
#include "core/drv.h"
#include "core/dev.h"

#if (DEVICE_DEBUG > DEVICE_DEBUG_OFF)
static const char *TAG = "BUS";
#endif
DEVICE_PUBLIC(static bus_t *) bus_table[BUS_NUM_MAX] = {0};

DEVICE_PUBLIC(int) bus_register(bus_t *bus) {
    if (!bus || !bus->name) {
        DEVICE_LOG_ERROR(TAG, "bus_register param invalid");
        return -1;
    }
    const int bus_max_num = DEVICE_ARRAY_SIZE(bus_table);
    for (int i = 0; i < bus_max_num; i++) {
        if (bus_table[i]) {
            continue;
        }
        bus_table[i] = bus;
        DEVICE_LOG_INFO(TAG, "bus[%s] register success", bus->name);
        return 0;
    }
    DEVICE_LOG_ERROR(TAG, "bus[%s] register error", bus->name);
    return -2;
}

DEVICE_PUBLIC(int) bus_unregister(bus_t *bus) {
    if (!bus) {
        DEVICE_LOG_ERROR(TAG, "bus_unregister param invalid");
        return -1;
    }
    const int bus_max_num = DEVICE_ARRAY_SIZE(bus_table);
    for (int i = 0; i < bus_max_num; i++) {
        if (bus_table[i] == bus) {
            bus_table[i] = NULL; 
            DEVICE_LOG_INFO(TAG, "bus[%s] unregister success", bus->name);
            return 0;
        }
    }
    DEVICE_LOG_ERROR(TAG, "bus[%s] unregister error", bus->name);
    return -2;
}


DEVICE_PUBLIC(static bus_t *) bus_find(bus_t *bus) {
    if (!bus) {
        DEVICE_LOG_ERROR(TAG, "bus_find param invalid");
        return NULL;
    }
    const int bus_max_num = DEVICE_ARRAY_SIZE(bus_table);
    for (int i = 0; i < bus_max_num; i++) {
        if (bus_table[i] == bus) {
            // DEVICE_LOG_DEBUG(TAG, "bus[%s] find success", bus->name);
            return bus_table[i];
        }
    }
    // DEVICE_LOG_ERROR(TAG, "bus[%s] find error", bus->name);
    return NULL;
}



DEVICE_PUBLIC(static driver_t *) find_driver_on_bus(bus_t *bus, driver_t *drv) {
    if (!bus || !drv) {
        DEVICE_LOG_ERROR(TAG, "find_driver_on_bus param invalid");
        return NULL;
    }
    driver_t *cur = (driver_t *)bus->driver;
    while (cur) {
        if (cur == drv) {
            // DEVICE_LOG_DEBUG(TAG, "driver [%s] found on bus [%s]", cur->name, bus->name);
            return cur;
        }
        if (cur->name && drv->name && strcmp(cur->name, drv->name) == 0) {
            // DEVICE_LOG_DEBUG(TAG, "driver [%s] found on bus [%s]", cur->name, bus->name);
            return cur;
        }
        cur = cur->next;
    }
    // DEVICE_LOG_DEBUG(TAG, "driver [%s] not found on bus [%s]", drv->name, bus->name);
    return NULL;
}

DEVICE_PUBLIC(int) bus_add_driver(bus_t *bus, void *driver) {
    if (!bus || !driver) {
        DEVICE_LOG_ERROR(TAG, "bus_add_driver param invalid");
        return -1;
    }

    bus_t *this_bus = bus_find(bus);
    if (!this_bus) {
        if (bus_register(bus) != 0) {
            return -2;
        }
        this_bus = bus_find(bus);
        if (!this_bus) {
            DEVICE_LOG_ERROR(TAG, "bus_find after register failed");
            return -3;
        }
    }

    driver_t *add_drv = (driver_t *)driver;

    if (find_driver_on_bus(this_bus, add_drv)) {
        DEVICE_LOG_ERROR(TAG, "bus add drv error: same driver already on bus");
        return -4;
    }

    add_drv->bus = this_bus;
    add_drv->next = (driver_t *)this_bus->driver;
    this_bus->driver = add_drv;
    // DEVICE_LOG_DEBUG(TAG, "add driver->[%s] to bus->[%s] success", add_drv->name, bus->name);

    // 遍历总线设备链表，匹配并调用 probe
    device_t *cur_dev = (device_t *)this_bus->device;
    while (cur_dev) {
        if (this_bus->match && this_bus->match(cur_dev, add_drv) == 0) {
            // DEVICE_LOG_DEBUG(TAG, "driver->[%s] matched device->[%s]", add_drv->name, cur_dev->name);
            cur_dev->drv = add_drv;
            cur_dev->ops = add_drv->ops;
            if (add_drv->probe) {
                add_drv->probe(cur_dev, add_drv);
            }
            // 如果驱动可支持多个设备，继续遍历，不用 break
        }
        cur_dev = cur_dev->next;
    }

    return 0;
}


DEVICE_PUBLIC(int) bus_remove_driver(bus_t *bus, void *driver) {
    if (!bus || !driver) {
        DEVICE_LOG_ERROR(TAG, "bus_remove_driver param invalid");
        return -1;
    }

    bus_t *this_bus = bus_find(bus);
    if (!this_bus) {
        DEVICE_LOG_ERROR(TAG, "bus not found: %s", bus->name);
        return -2;
    }

    driver_t *target = (driver_t *)driver;
    driver_t *prev = NULL;
    driver_t *cur = (driver_t *)this_bus->driver;

    while (cur) {
        if (cur == target) {
            if (prev) {
                prev->next = cur->next;
            } else {
                this_bus->driver = cur->next;
            }
            target->bus = NULL;
            // DEVICE_LOG_DEBUG(TAG, "remove drv->[%s] from bus->[%s] success", target->name, this_bus->name);
            return 0;
        }

        prev = cur;
        cur = cur->next;
    }

    // DEVICE_LOG_ERROR(TAG, "driver [%s] not found on bus [%s]", target->name, this_bus->name);
    return -3; 
}

DEVICE_PUBLIC(static device_t *) find_device_on_bus(bus_t *bus, device_t *dev){
    if (!bus || !dev) {
        DEVICE_LOG_ERROR(TAG, "find_device_on_bus param invalid");
        return NULL;
    }
    device_t *cur = (device_t *)bus->device;
    while (cur) {
        if (cur == dev) {
            // DEVICE_LOG_DEBUG(TAG, "device [%s] found on bus [%s]", cur->name, bus->name);
            return cur;
        }
        if (cur->name && dev->name && strcmp(cur->name, dev->name) == 0) {
            // DEVICE_LOG_DEBUG(TAG, "device [%s] found on bus [%s]", cur->name, bus->name);
            return cur;
        }
        cur = cur->next;
    }

    // DEVICE_LOG_ERROR(TAG, "device [%s] not found on bus [%s]", dev->name, bus->name);
    return NULL;
}



DEVICE_PUBLIC(int) bus_add_device(bus_t *bus, void *device) {
    if (!bus || !device) {
        DEVICE_LOG_ERROR(TAG, "bus_add_device param invalid");
        return -1;
    }

    bus_t *this_bus = bus_find(bus);
    if (!this_bus) {
        if (bus_register(bus) != 0) {
            return -2;
        }
        this_bus = bus_find(bus);
        if (!this_bus) {
            // DEVICE_LOG_ERROR(TAG, "bus_find after register failed");
            return -3;
        }
    }
    device_t *add_dev = (device_t *)device;

    if (find_device_on_bus(this_bus, add_dev)) {
        // DEVICE_LOG_ERROR(TAG, "bus add dev error: same device already on bus");
        return -4;
    }
    add_dev->bus = this_bus;
    add_dev->next = (device_t *)this_bus->device;
    this_bus->device = add_dev;
    // DEVICE_LOG_DEBUG(TAG, "add dev->[%s] to bus->[%s] success", add_dev->name, bus->name);
    driver_t *cur_drv = (driver_t *)this_bus->driver;
    while (cur_drv) {
        if (this_bus->match && this_bus->match(add_dev, cur_drv) == 0) {
            // DEVICE_LOG_DEBUG(TAG, "add dev->[%s] match drv->[%s] success", add_dev->name, cur_drv->name);
            add_dev->drv = cur_drv;
            add_dev->ops = cur_drv->ops;
            if (cur_drv->probe) {
                cur_drv->probe(add_dev, cur_drv);
            }
            break;
        }
        cur_drv = cur_drv->next;
    }

    return 0;
}

DEVICE_PUBLIC(int) bus_remove_device(bus_t *bus, void *device) {
    if (!bus || !device) {
        DEVICE_LOG_ERROR(TAG, "bus_remove_device param invalid");
        return -1;
    }

    bus_t *this_bus = bus_find(bus);
    if (!this_bus) {
        DEVICE_LOG_ERROR(TAG, "bus not found: %s", bus->name);
        return -2;
    }

    device_t *target = (device_t *)device;
    device_t *prev = NULL;
    device_t *cur = (device_t *)this_bus->device;

    while (cur) {
        if (cur == target) {
            if (prev) {
                prev->next = cur->next;
            } else {
                this_bus->device = cur->next;
            }
            driver_t *drv = (driver_t *)cur->drv;
            if (drv && drv->remove) {
                drv->remove(cur, drv);
            }
            cur->bus = NULL;
            cur->drv = NULL;
            // DEVICE_LOG_DEBUG(TAG, "remove dev->[%s] from bus->[%s] success", target->name, this_bus->name);
            return 0;
        }
        prev = cur;
        cur = cur->next;
    }

    // DEVICE_LOG_ERROR(TAG, "device [%s] not found on bus [%s]", target->name, this_bus->name);
    return -3;
}
